import * as THREE from 'three';
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js';
import { FontLoader, Font } from  'three/examples/jsm/loaders/FontLoader.js';
/*单组配置
{
        vm:this,
        el:this.$el,
        width:this.$parent.$el.clientWidth,
        height:this.$parent.$el.clientHeight,
        environment:{
          s:110,
          render:{
            clearColor:0xffffff,
            opacity:1
          },
          helper:{plane:false,camera:false,axes:true},
          point:[{x:-200,y:100,z:200,c:'#EAEAEF'}],
          ambient:{c:0x888888},
          camera:{type:'OrthographicCamera',x:-400,y:200,z:-200,n:1,f:1000},
        }，
        element:{
            name:'',
            geo:[],
            material:[],
            mesh:[]
        }
      }
 */
/*多组配置
{
        vm:this,
        el:this.$el,
        width:this.$parent.$el.clientWidth,
        height:this.$parent.$el.clientHeight,
        environment:{
          s:110,
          render:{
            clearColor:0xffffff,
            opacity:1
          },
          helper:{plane:false,camera:false,axes:true},
          point:[{x:-200,y:100,z:200,c:'#EAEAEF'}],
          ambient:{c:0x888888},
          camera:{type:'OrthographicCamera',x:-400,y:200,z:-200,n:1,f:1000},
        }，
        element:[
            {
                name:'',
                position:{},
                config:{},
            }
        ]
      }
 */
export default class starThree {
    constructor(config) {
        this.config=config;//单组配置与多组配置结构不同
        this.vm=this.config.vm;//渲染vnode的this
        this.el=this.config.el;//渲染的dom
        this.scene=new THREE.Scene();//创建场景
        /**
         * 设置三维环境
         */
        this.addHelper(this.config.environment.helper);//添加辅助线
        this.addPoint(this.config.environment.point);//添加点光源
        this.addAmbient(this.config.environment.ambient);//添加环境光
        this.camera=this.addCamera(this.config.environment.camera);//添加摄像机

    }
    /**
     * 创建新对象
     * @param config
     * @returns {starThree}
     */
    static create(config){
        return new starThree(config)
    }
    /****************************************************************************************
     * 设置整页方法
     ****************************************************************************************/
    /**
     * 设置整页三维页面
     */
    setPageScene(){
        this.addPlant(this.config.plant);//添加平面
        this.addPage();//添加页面元素
        this.addConnect();
        this.addMoveItem();
        this.addAnimation(this.config.anim);
        this.addRenderer(this.config.environment.render);//启动渲染
        this.addControl();//添加控制及事件
    }
    /**
     * 增加平板
     * @param plant
     */
    addPlant(plant){
        let geo=new THREE.BoxGeometry(plant.width,plant.height,plant.depth);
        let material=new THREE.MeshPhongMaterial({color:plant.color, specular:0xffffff, shininess:80});
        let mesh=new THREE.Mesh( geo, material );
        let group = new THREE.Group();
        group.name='plant';
        group.add(mesh);
        this.scene.add(group);
    }
    /**
     * 添加页面元素
     */
    addPage(){
        if(!this.config.element){
            return;
        }
        this.config.element.map(item=>{
            if(!item.config){
                return;
            }
            let group=this.addGroup(item.config);
            group.name=item.name;
            group.position.set(item.position.x,item.position.y,item.position.z);
            if(!!item.position.rx&&item.position.rx!==0){
                group.rotateX(Math.PI/(360/item.position.rx));
            }
            if(!!item.position.ry&&item.position.ry!==0){
                group.rotateY(Math.PI/(360/item.position.ry));
            }
            if(!!item.position.rz&&item.position.rz!==0){
                group.rotateZ(Math.PI/(360/item.position.rz));
            };
            group.scale.set(item.position.scaleX,item.position.scaleY,item.position.scaleZ);
            this.scene.add(group);
        });
    }

    /**
     * 添加连接模型
     */
    addConnect(){
        if(!this.config.connect){
            return;
        }
        this.config.connect.map(item=>{
            this.scene.add(this._getConnect(item))
        })
    }
    addMoveItem(){
        if(!this.config.moveItem){
            return;
        }
        this.config.moveItem.map(item=>{
            this.scene.add(this._getBlock(item))
        })
    }
    /****************************************************************************************
     * 设置单组配置方法
     ****************************************************************************************/
    /**
     * 设置单组配置
     */
    setElementScene(){
        this.group=this.addGroup(this.config.element);//添加元素组
        this.group.name=this.config.element.name;//设置组名称
        this.scene.add(this.group);//添加入场景
        this.addRendererWithoutAnim(this.config.environment.render);//启动渲染
        this.addControl();//添加控制及事件
    }
    /**
     * 创建元素组
     * @param element
     * @returns {Group}
     */
    addGroup(element){
        let geo={};
        let material={};
        let group = new THREE.Group();
        element.geo.map(item=>{
            geo[item.name]=this._getGeo(item);
        });
        element.material.map(item=>{
            material[item.name]=this._getMaterial(item);
        })
        element.mesh.map(item=>{
            let mesh=this._getMesh(geo[item.geo],material[item.material],item)
            group.add(mesh);
        });
        return group;
    }
    /****************************************************************************************
     * 设置三维场景方法
     ****************************************************************************************/
    /**
     * 添加辅助线
     * @param helper
     */
    addHelper(helper){
        if(!!helper){
            if(!!helper.plane){
                let plane = new THREE.Plane( new THREE.Vector3( 1, 0, 0 ), 3 );
                this.scene.add( new THREE.PlaneHelper( plane, 500, 0xffff00 ) );
            };
            if(!!helper.camera){
                let camera = new THREE.PerspectiveCamera( 75, window.innerWidth / window.innerHeight, 0.1, 1000 );
                this.scene.add(new THREE.CameraHelper(camera));
            };
            if(!!helper.axes){//用于简单模拟3个坐标轴的对象.红色代表 X 轴. 绿色代表 Y 轴. 蓝色代表 Z 轴.
                this.scene.add(new THREE.AxesHelper( 500 ));
            }
        }
    }
    /**
     * 点光源
     * @type {PointLight|PointLight}
     */
    addPoint(points){
        points.map(p=>{
            let point=new THREE.PointLight(p.c);
            point.position.set(p.x,p.y,p.z);
            this.scene.add(point);
        })
    }
    /**
     * 环境光
     * @type {AmbientLight|AmbientLight}
     */
    addAmbient(ambient){
        this.scene.add(new THREE.AmbientLight(ambient.c))
    }
    /**
     * 创建相机对象
     * @type {OrthographicCamera|OrthographicCamera}
     */
    addCamera(item){
        let s=this.config.environment.s;
        let k=this.config.width/this.config.height;
        if(item.type==='OrthographicCamera'){
            let camera=new THREE.OrthographicCamera(-s*k,s*k,s,-s,item.n,item.f);
            camera.position.set(item.x,item.y,item.z);
            camera.lookAt(this.scene.position);
            this.scene.add(camera);
            return camera;
        };
        return null;
    }
    /**
     * 创建渲染器对象
     * @type {WebGL1Renderer|WebGL1Renderer}
     */
    addRenderer(render){
        this.renderer=new THREE.WebGL1Renderer({antialias: true});
        this.renderer.setSize(this.config.width,this.config.height);
        this.renderer.setClearColor(render.clearColor,render.opacity);
        this.el.appendChild(this.renderer.domElement);
        let clock=new THREE.Clock();
        this.myRender=()=>{
            requestAnimationFrame(this.myRender);
            this.mixer.update(clock.getDelta())
            this.renderer.render(this.scene,this.camera);
        };
        this.myRender();
    }
    addRendererWithoutAnim(render){
        this.renderer=new THREE.WebGL1Renderer({antialias: true});
        this.renderer.setSize(this.config.width,this.config.height);
        this.renderer.setClearColor(render.clearColor,render.opacity);
        this.el.appendChild(this.renderer.domElement);
        this.myRender=()=>{
            this.renderer.render(this.scene,this.camera);
        };
        this.myRender();
    }
    addAnimation(clips){
        this.mixer=new THREE.AnimationMixer(this.scene);
        clips.map(item=>{
            let actions=[];
            item.action.map(act=>{
                let anim;
                if(act.name==='color'){
                    let color1 = new THREE.Color(act.values[0]);
                    let color2 = new THREE.Color(act.values[1]);
                    let colorArray=Object.values(color1).concat(Object.values(color2));
                    anim=new THREE.ColorKeyframeTrack(item.name+'.material.'+act.name,act.times,colorArray);
                }else{
                    anim=new THREE.KeyframeTrack(item.name+'.'+act.name,act.times,act.values);
                }
                actions=actions.concat(anim);
            })
            let clip=new THREE.AnimationClip('default',item.duration,actions);
            let animation=this.mixer.clipAction(clip);
            animation.play();
        })
    }
    /**
     * 添加控制事件
     */
    addControl(){
        let controls=new OrbitControls(this.camera,this.renderer.domElement);
        controls.addEventListener('change',this.myRender);
        this.renderer.domElement.addEventListener(
            "dblclick",e=>{
                //获取raycaster和所有模型相交的数组，其中的元素按照距离排序，越近的越靠前
                let intersects = this.getIntersects(event);
                //获取选中最近的Mesh对象
                //instance坐标是对象，右边是类，判断对象是不是属于这个类的
                if (intersects.length !== 0 && intersects[0].object.type === 'Mesh') {
                    let selectObject = intersects[0].object;
                    //changeMaterial(selectObject)
                    let name=!!selectObject.parent?selectObject.parent.name:selectObject.name;
                    alert(name)
                    console.log(selectObject)
                } else {
                    console.log('未选中 Mesh!');
                }
            })
    }
    //获取与射线相交的对象数组
    getIntersects(event) {
        event.preventDefault();// 阻止默认的点击事件执行, https://developer.mozilla.org/zh-CN/docs/Web/API/Event/preventDefault
        //console.log("eventModel.clientX:" + eventModel.clientX);
        //console.log("eventModel.clientY:" + eventModel.clientY);

        //声明 rayCaster 和 mouse 变量
        let rayCaster = new THREE.Raycaster();
        let mouse = new THREE.Vector2();
        let canvas=this.renderer.domElement;
        //通过鼠标点击位置，计算出raycaster所需点的位置，以屏幕为中心点，范围-1到1
        mouse.x = ((event.clientX - canvas.getBoundingClientRect().left) / canvas.offsetWidth) * 2 - 1;
        mouse.y = -((event.clientY - canvas.getBoundingClientRect().top) / canvas.offsetHeight) * 2 + 1; //这里为什么是-号，没有就无法点中

        //通过鼠标点击的位置(二维坐标)和当前相机的矩阵计算出射线位置
        rayCaster.setFromCamera(mouse, this.camera);

        //获取与射线相交的对象数组， 其中的元素按照距离排序，越近的越靠前。
        //+true，是对其后代进行查找，这个在这里必须加，因为模型是由很多部分组成的，后代非常多。
        let intersects = rayCaster.intersectObjects(this.scene.children, true);

        //返回选中的对象
        return intersects;
    }
    /****************************************************************************************
     * 创建三维模型
     ****************************************************************************************/
    /**
     * 创建三维形状
     * @param item
     * @returns {null}
     * @private
     */
    _getGeo(item){
        let geo=null;
        switch (item.type){
            case 'box':
                geo=new THREE.BoxGeometry(item.width,item.height,item.depth);
                break;
            case 'cylinder':
                geo=new THREE.CylinderGeometry(item.top,item.bottom,item.height,item.segment);
                break;
            case 'sphere':
                geo=new THREE.SphereGeometry(item.r,item.segment,item.segment);
                break;
        };
        return geo;
    }
    /**
     * 创建材质
     * @param item
     * @returns {null}
     * @private
     */
    _getMaterial(item){
        let material=null;
        switch (item.type){
            case 'lambert':
                material=new THREE.MeshPhongMaterial({color:item.color, specular:0xffffff, shininess:80});
                break;
        };
        return material;
    }
    /**
     * 组合三维模型
     * @param geo
     * @param material
     * @param item
     * @returns {Mesh}
     * @private
     */
    _getMesh(geo,material,item){
        let mesh=new THREE.Mesh( geo, material );
        mesh.position.set(item.x,item.y,item.z);
        if(!!item.rx&&item.rx!==0){
            mesh.rotateX(Math.PI/(360/item.rx));
        }
        if(!!item.ry&&item.ry!==0){
            mesh.rotateY(Math.PI/(360/item.ry));
        }
        if(!!item.rz&&item.rz!==0){
            mesh.rotateZ(Math.PI/(360/item.rz));
        };
        return mesh;
    }
    /****************************************************************************************
     * 缺省三维模型
     ****************************************************************************************/
    /**
     * 辊道
     * @param item
     * @returns {Group}
     * @private
     */
    _getConnect(item){
        let geo=this._getGeo({type:'box',width:2,height:5,depth:item.length});
        let material=this._getMaterial({type:'lambert',color:item.color});
        let mesh1=this._getMesh(geo,material,{x:0,y:0,z:0})
        let mesh2=mesh1.clone();
        mesh2.position.set(10,0,0);
        let group = new THREE.Group();
        group.add(mesh1,mesh2)
        let rollGeo=this._getGeo({type:'cylinder',top:1,bottom:1,height:13,segment:500});
        let rollMaterial=this._getMaterial({type:'lambert',color:item.intervalColor});
        let inter=parseInt(item.length/(item.interval+1));
        for(let i=1;i<item.interval+1;i++){
            let roll=this._getMesh(rollGeo,rollMaterial,{x:5,y:0,z:i*inter-item.length/2});
            roll.rotateZ(Math.PI/2);
            group.add(roll);
        }
        group.position.set(item.x,item.y,item.z);
        group.name=item.name;
        if(!!item.rx&&item.rx!==0){
            group.rotateX(Math.PI/(360/item.rx));
        }
        if(!!item.ry&&item.ry!==0){
            group.rotateY(Math.PI/(360/item.ry));
        }
        if(!!item.rz&&item.rz!==0){
            group.rotateZ(Math.PI/(360/item.rz));
        };
        group.scale.set(item.scaleX,item.scaleY,item.scaleZ);
        return group;
    }
    _getBlock(item){
        let geo=this._getGeo({type:'box',width:item.width,height:item.height,depth:item.depth});
        let material=this._getMaterial({type:'lambert',color:item.color});
        let mesh=this._getMesh(geo,material,{x:0,y:0,z:0})
        mesh.name=item.name;
        mesh.position.set(item.x,item.y,item.z);
        // let group = new THREE.Group();
        // group.add(mesh);
        // group.position.set(item.x,item.y,item.z);
        // group.name=item.name;
        return mesh;
    }
    /**
     *
     * @param labels
     */
    addLabel(labels){
        let _this=this;
        Object.keys(labels).map(label=>{
            let gObject=null;
            let mObject=null;
            if(labels[label].gType==='plane'){
                gObject=new THREE.PlaneGeometry(labels[label].w,labels[label].h);
            }
            if(labels[label].mType==='image'){
                mObject=new _THREE.MeshBasicMaterial({
                    color: 'white',//labels[label].c,
                    side: THREE.DoubleSide,
                    map:new THREE.TextureLoader().load(this.getTextCanvas(labels[label].w,labels[label].h,labels[label].c,labels[label].t)),
                    opacity: 0.5,
                    // transparent:true
                })
                let mesh=new _THREE.Mesh( gObject, mObject )
                mesh.position.set(labels[label].x,labels[label].y,labels[label].z);
                if(!!labels[label].rx){
                    mesh.rotateX(labels[label].rx)
                }
                if(!!labels[label].ry){
                    mesh.rotateY(labels[label].ry)
                }
                if(!!labels[label].rz){
                    mesh.rotateZ(labels[label].rz)
                }
                mesh.name='label_'+label;
                _this.scene.add( mesh );
            }
        })
    }


    addText(){
        let text='furnace';

        let loader = new FontLoader();
        let _this=this;
        loader.load("three/examples/fonts/helvetiker_bold.typeface.json", function (res) {
            let geometry = new THREE.TextGeometry(`${text}`, {
                font: res,          // 字体格式
                size: 13,           // 字体大小
                height: 1,          // 字体深度
                curveSegments: 11,  // 曲线控制点数
                bevelEnabled: true, // 斜角
                bevelThickness: 0.1,  // 斜角的深度
                bevelSize: 1,       // 斜角的大小
                bevelSegments: 1    // 斜角段数
            });
            let mat = new THREE.MeshPhongMaterial({
                color: "white",
                opacity: 0.8,
                shininess: 1,
            });
            let mesh = new THREE.Mesh(geometry, mat);
            mesh.rotation.y = -Math.PI / 2
            mesh.position.set(-151, 150 - 40 * index, 15.1);
            _this.scene.add(mesh);
        })
    }
    getTextCanvas(w,h,c,text){
        //用canvas生成图片
        let canvas = document.createElement("canvas");
        let ctx = canvas.getContext('2d')
        canvas.width = w*300
        canvas.height = h*300
        //制作矩形
        ctx.fillStyle = c;
        ctx.fillRect(0, 0, w*300, h*300)
        //设置文字
        ctx.fillStyle = "white";
        ctx.font = 'normal 1500px "楷体"'
        ctx.fillText(text, w*20, h*300/2)

        //生成图片
        let url = canvas.toDataURL('image/png');
        return url;
    }
    getTextCanvas2(){
        //用canvas生成图片
        let canvas = document.createElement("canvas");
        let ctx = canvas.getContext('2d')
        canvas.width = 300
        canvas.height = 300
        //制作矩形
        ctx.fillStyle = "gray";
        ctx.fillRect(0, 0, 300, 300)
        //设置文字
        ctx.fillStyle = "white";
        ctx.font = 'normal 3px "楷体"'
        ctx.strokeText('furnace', 0, 2)

        //生成图片
        let url = canvas.toDataURL('image/png');
        //将图片构建到纹理中
        let geometry1 = new THREE.PlaneGeometry(15, 10)
        let material1 = new THREE.MeshBasicMaterial({
            map: new THREE.TextureLoader().load(url),
            side: THREE.DoubleSide,
            opacity: 1
        })
        let rect = new THREE.Mesh(geometry1, material1)
        rect.position.set(0, 0, 25.1)
        rect.rotateY(-Math.PI/2)
        this.scene.add(rect)
    }

}